package fr.upmc.i3s.fuglmeyeranalyser.tools.utils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.security.GeneralSecurityException;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
import com.sun.org.apache.xml.internal.security.utils.Base64;

public class EncryptUtils {

	//Ciphering (encryption and decryption) password/key.
	private static final char[] PASSWORD = "Unauthorized_Personel_Is_Unauthorized".toCharArray();
	
	//Cipher salt.
	private static final byte[] SALT = {
		(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
		(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,};
	
	public static void encryptPassword(String file, char[] password, int layers) throws GeneralSecurityException, FileNotFoundException, IOException {
		StringBuilder sb = new StringBuilder();
		//First encryption layer. Encrypts secure attribute values only.
		String encryptedValue;
		if (layers >= 1) {
			encryptedValue = encrypt(password);
		} else {
			encryptedValue = new String(password);
		}
		sb.append(encryptedValue);

		//Prepare file and file-writing process.
		File f = new File(file);
		if (!f.getParentFile().exists()) {
			f.getParentFile().mkdirs();
		} else if (f.exists()) {
			f.delete();
		}
		BufferedWriter bw = new BufferedWriter(new FileWriter(f));
		//Second encryption layer. Encrypts whole file content including previously encrypted stuff.
		if (layers >= 2) {
			bw.append(encrypt(sb.toString().trim()));
		} else {
			bw.append(sb.toString().trim());
		}
		bw.flush();
		bw.close();
	}
	
	/**
	 * String decryptedContent = decryptPassword(DOUBLE_LAYER);
	 */
	public static String decryptPassword(String file) throws FileNotFoundException, IOException, GeneralSecurityException {
		File f = new File(file);
		BufferedReader br = new BufferedReader(new FileReader(f));
		StringBuilder sb = new StringBuilder();
		while (br.ready()) {
			sb.append(br.readLine()).append(System.lineSeparator());
		}
		br.close();
		return decrypt(sb.toString());
	}

	
	  ///////////////////
	 // ENCRYPT UTILS //
	///////////////////
	
	private static String encrypt(byte[] property) throws GeneralSecurityException {
		SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
		SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD));
		Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
		pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(SALT, 20));

		//Encrypt and save to temporary storage.
		String encrypted = Base64.encode(pbeCipher.doFinal(property));

		//Cleanup data-sources - Leave no traces behind.
		for (int i = 0; i < property.length; i++) {
			property[i] = 0;
		}
		property = null;
		System.gc();

		//Return encryption result.
		return encrypted;
	}

	private static String encrypt(char[] property) throws GeneralSecurityException {
		//Prepare and encrypt.
		byte[] bytes = new byte[property.length];
		for (int i = 0; i < property.length; i++) {
			bytes[i] = (byte) property[i];
		}
		String encrypted = encrypt(bytes);
		return encrypted;
	}

	private static String encrypt(String property) throws GeneralSecurityException {
		String encrypted = encrypt(property.getBytes());
		property = null;
		return encrypted;
	}

	
	  ///////////////////
	 // DECRYPT UTILS //
	///////////////////	
	
	private static String decrypt(String property) throws GeneralSecurityException, IOException {
		SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
		SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD));
		Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
		pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
		try {
			return new String(pbeCipher.doFinal(Base64.decode(property)));
		} catch (Base64DecodingException e) {
			e.printStackTrace();
		}
		return null;
	}

}
